home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / util / Leaks-dcmd.sit / Leaks dcmd / Leaks.a < prev    next >
Text File  |  1991-10-09  |  23KB  |  476 lines

  1. ; Leaks.a
  2. ;
  3. ;        Copyright © 1990-91 by Apple Computer, Inc., all rights reserved.
  4. ;
  5. ;    by     Bo3b Johnson    10/9/90
  6. ;        MS 37-DS
  7. ;
  8. ;    This file is the assembly routines I need in order to interface to the toolbox in
  9. ;    a sensible fashion.  The OS is funky, and there is no good way to do these things
  10. ;    from Pascal or C.  The use of INLINE code is OK, but not always enough.
  11. ;
  12. ;    In order to fashion a good patch to NewPtr/NewHandle, I need to use PC
  13. ;    relative addressing.  The basic problem is that there is no good way to get back
  14. ;    to the dcmd globals, while the patch is executing.  Certainly you can't rely upon
  15. ;    A5, A6, or A7; and no other registers are available for use.  I could stick something
  16. ;    into AppParms, CurApName, or a number of other atrocities, but I don't own
  17. ;    any of those spots, and could quite easily end up competing with some other code
  18. ;    for the spot.  In the past, this code patched the Chain trap, since it is not in use,
  19. ;    but that has competition problems too.  If I use PC-relative addressing to get to
  20. ;    some global information, then this becomes self-modifying code, since I will
  21. ;    change the code in this block, using a piece of code space as variable storage.  
  22. ;    This seems the most risk-free version for now, notwithstanding certain strident
  23. ;    if misguided warnings from various groups.  It's not enough to tell me not to
  24. ;    do something, you have to give me an alternative.  If you have a better alternative,
  25. ;    by all means let me know.
  26. ;
  27. ;    I patch the four traps of NewPtr/DisposPtr, NewHandle/DisposHandle; and all
  28. ;    these pieces are almost identical.  The only difference really is the variable being
  29. ;    used.  The basic idea is to have one routine to save off the old address, gotten from
  30. ;    an NGetTrapAddress; and to save that into the code here, using PC-relative addressing.
  31. ;    The trap is patched by the Pascal code, and whenever it gets called, it will call here
  32. ;    to have us call through the old version of the trap.  That way it gets control before
  33. ;     the trap executes, but I still drive the old code. 
  34. ;
  35. ;    Realize that the code in this file is getting run mostly during the normal operation
  36. ;    of the Mac.  The stack is whatever the current app is using, as well as that A5 world,
  37. ;    and stack crawl.  I don't where I'll be so, I can't rely on those things.  This
  38. ;    is very different from the code in the dcmd, where it knows the stack it is using is
  39. ;    in Macsbug, and very small.  Pieces of this code will get run a tiny bit from the dcmd,
  40. ;    since it will call here in order to get access to my global variables, the treeTop, emptyQ,
  41. ;    and activeState.  
  42. ;
  43. ;    One of the goals in this file is to avoid needing an asm version of the record structure
  44. ;    I use.  I really just want to pass around pointers, so that this file can be a little
  45. ;    more independent of the basic structures.  One of the things that's get used though is
  46. ;    the size of the Stack Crawl array that I pass around.  There is a constant here, but
  47. ;    if it changes in the Pascal code, it should change here.
  48.  
  49. ;     By the way, this file looks best if viewed in Palatino 12.
  50.  
  51.     
  52.             include        'SysErr.a'
  53.             include        'SysEqu.a'
  54.             include        'Traps.a'
  55.  
  56.             proc
  57.             import        AddNewBlock, KillOldBlock
  58.  
  59. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  60. ;
  61. ; Storage for the old patch addresses, used to call through once the patch code executes.
  62. ;    These are essentially globals, used by the asm code.  They are specifically not exported,
  63. ;    so that the Pascal code cannot access them directly.  There are a number of interface
  64. ;    routines I set up so that Pascal can get and set them, but has to go through this file.
  65. ;    You know, sort of object like.
  66. ;
  67. pOldNewPtr                dc.l        0
  68. pOldNewHandle        dc.l        0
  69. pOldDisposPtr            dc.l        0
  70. pOldDisposHandle        dc.l        0
  71.  
  72. pEmptyQ                    dc.l        0                    ; list of empty elements available.
  73. pTreeTop                dc.l        0                    ; list of active elements.
  74. pActive                    dc.w        0                    ; whether to watch blocks or not.
  75.  
  76.  
  77. saveEm                    reg        a0-a3/d0-d3        ; a few to keep, since I'm a patch.
  78. saveEmSize                EQU        (8*4)                ; 8 registers times 4 bytes each.
  79.  
  80.  
  81. kCrawlArraySize        EQU        8                    ; number of stack crawl elements to do.
  82.  
  83.  
  84. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  85. ; When I'm am setting up the world, I call NGetTrapAddress to get the old version
  86. ; of the traps.  I need to save that dude off so I can get back there when needed. 
  87. ; This routine is a handy interface to the high-level world, isolating this asm junk
  88. ; from the code.   All these routines are the same, just a different variable being affected.
  89. ; This hunk uses the PC-Relative addressing mode in order to get the address of the
  90. ; variable being set.  This allows the code to function without any explicit global
  91. ; space, since the code acts like globals here.
  92. ; The interface is:
  93. ;    PROCEDURE  SetOldNewPtr (address: LongInt);
  94. ;    PROCEDURE  SetOldNewHandle (address: LongInt);
  95. ;    PROCEDURE  SetOldDisposPtr (address: LongInt);
  96. ;    PROCEDURE  SetOldDisposHandle (address: LongInt);
  97.  
  98.             export        SetOldNewPtr, SetOldNewHandle, SetOldDisposPtr, SetOldDisposHandle
  99.  
  100. SetOldNewPtr
  101.             LEA            pOldNewPtr,A1            ; the variable to be setting
  102.             BRA.S        Common                    ; do common stuffing code.
  103. SetOldNewHandle
  104.             LEA            pOldNewHandle,A1        ; the variable to set
  105.             BRA.S        Common                    ; do common stuffing code.
  106. SetOldDisposPtr
  107.             LEA            pOldDisposPtr,A1            ; the variable to do
  108.             BRA.S        Common                    ; do common stuffing code.
  109. SetOldDisposHandle
  110.             LEA            pOldDisposHandle,A1        ; the variable 
  111. Common        
  112.             MOVE.L        (SP)+,A0                        ; get the return address.
  113.             MOVE.L        (SP)+,(A1)                    ; save it, pulling parameter too.
  114.             JMP            (A0)                            ; it's saved, return to high-level.
  115.  
  116.  
  117. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  118. ;    These routines are used by the dcmd in order to get to the global variables, to find the
  119. ;    treeTop, emptyQ, and activeState.  They aren't used by the b-tree management stuff,
  120. ;    since I always pass these variables to those routines.  In addition, the patch code above
  121. ;    doesn't use this code, since it has direct access to the variables, and does so, using the
  122. ;    pc-relative approach.  
  123. ;
  124. ;
  125. ; GetTreeInfo:
  126. ;    When desired, the pascal callers may need to get the tree info in order to be able to drive
  127. ;    the trees for info display reasons.  This is an interface to let them see us, but I get to
  128. ;     pass back the info, rather than have them groping around directly for the data.
  129. ;        FUNCTION GetTreeTop: TrackEntryPtr;     EXTERNAL;
  130. ;        FUNCTION GetEmptyQ: TrackEntryPtr;     EXTERNAL;
  131. ;        FUNCTION    TrackActive: Boolean;    EXTERNAL;
  132.  
  133.             export        GetTreeTop, GetEmptyQ, TrackActive
  134.  
  135. ; GetTreeTop:
  136. ;    This routine just returns the tree top as a parameter.  The assembly stuff just grabs
  137. ;    these guys directly when it needs to, but the Pascal code comes through this interface
  138. ;    to get the references.
  139. ;        FUNCTION GetTreeTop: TreeEntryPtr;     EXTERNAL;
  140.  
  141. GetTreeTop
  142.             MOVE.L        pTreeTop,4(SP)                ; stuff the return result (pc-relative for source.)
  143.             RTS                                            ; and return to caller.
  144.  
  145. ; GetEmptyQ:
  146. ;        FUNCTION GetEmptyQ: TreeEntryPtr;     EXTERNAL;
  147.  
  148. GetEmptyQ
  149.             MOVE.L        pEmptyQ,4(SP)                ; stuff the return result (pc-relative for source.)
  150.             RTS                                            ; and return to caller.
  151.  
  152. ; TrackActive:
  153. ;    Just return the active state as a boolean.  This is whether I am actively watching
  154. ;    and recording block addresses or not.  I always want to be able to turn it off sometimes,
  155. ;    since I want to save a given state for viewing.  
  156. ;        FUNCTION    TrackActive: Boolean;    EXTERNAL;
  157.  
  158. TrackActive
  159.             MOVE.W    pActive,4(SP)                ; stuff function result (pc-relative source).
  160.             RTS                                            ; and return to caller. 
  161.             
  162.  
  163. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  164. ; As part of the interface to the dcmd, I have setting routines too, to set the variables
  165. ; to a known state.  This way I don't have to put that sort of tree-based init code in 
  166. ; assembly.  I need to turn it on and off using the dcmd, so it will call here to do so.
  167. ;
  168. ;        PROCEDURE SetTreeTop (address: TrackEntryPtr);        EXTERNAL;
  169. ;        PROCEDURE SetEmptyQ (address: TrackEntryPtr);        EXTERNAL;
  170. ;        PROCEDURE SetActive (state: Boolean);        EXTERNAL;
  171.  
  172.  
  173.             export        SetTreeTop, SetEmptyQ, SetActive
  174.  
  175. ;        PROCEDURE SetTreeTop (address: TrackEntryPtr);        EXTERNAL;
  176.  
  177. SetTreeTop
  178.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  179.             LEA            pTreeTop,A1                ; the variable I need to set, pc-relative.
  180.             MOVE.L        (SP)+,(A1)                    ; save off the new variable, clearing parameter.
  181.             JMP            (A0)                            ; and return to caller.
  182.             
  183. ;        PROCEDURE SetEmptyQ (address: TrackEntryPtr);        EXTERNAL;
  184.  
  185. SetEmptyQ
  186.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  187.             LEA            pEmptyQ,A1                ; the variable I need to set, pc-relative.
  188.             MOVE.L        (SP)+,(A1)                    ; save off the new variable, clearing parameter.
  189.             JMP            (A0)                            ; and return to caller.
  190.             
  191. ;        PROCEDURE SetActive (state: Boolean);        EXTERNAL;
  192.  
  193. SetActive
  194.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  195.             LEA            pActive,A1                    ; the variable I need to set, pc-relative.
  196.             MOVE.W    (SP)+,(A1)                    ; save off the new state, clearing parameter.
  197.             JMP            (A0)                            ; and return to caller.
  198.             
  199.             
  200. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  201. ;    This is the patch code for the various pieces I patch.  This is in assembly
  202. ;     since I need to preserve registers and do some other low level jacking around,
  203. ;    like trying to do a stack crawl whenever I allocate blocks.  These routines are the
  204. ;     actual code that gets called when one of the four traps is executed.
  205.  
  206. ; WatchNewPtr:
  207. ;    The routine to patch the NewPtr trap, and I get first dibs at getting the info I want
  208. ;    out of the registers.  I need to save off the size to start with, since D0 gets pounded
  209. ;    to the return result once I actually call the old NewPtr code.  Once I have that
  210. ;    safely stowed on the stack, I call the old code, using the PC-relative addressing in
  211. ;    order to get the old address that I'm supposed to call.  This was set up when the
  212. ;    dcmd called me to save off the value.  When that old NewPtr junk returns, I get
  213. ;     the actual address allocated, if enough memory, then do the funky stack crawl,
  214. ;    and finally pass all these tidbits off to the AddNewBlock routine.
  215.  
  216.             export        WatchNewPtr
  217.             
  218.  
  219. WatchNewPtr
  220.             MOVE.L        D0,-(SP)                        ; save size off.
  221.  
  222. ; First, allocate the pointer.
  223.  
  224.             PEA            @1                            ; coming back here, as return address.
  225.             MOVE.L        pOldNewPtr,-(SP)            ; the old address I am going to,
  226.             RTS                                            ; JMP there, when it RTSes I come back
  227. @1                                                        ; --here.
  228.  
  229. ; A0 is set up as the handle to track, add it to the b-tree list.  If it is NIL, meaning the
  230. ; NewPtr failed, then don't add it to the list, it cannot be disposed.  I do the save of
  231. ; registers here, after the allocation of memory, so that I can save the registers as they
  232. ; are coming out of the call, not going in.
  233.  
  234.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  235.  
  236.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  237.             BEQ.S            @exit                            ; if not, skip saving.
  238.             CMP.L        #0,A0                            ; is it a NIL return?
  239.             BEQ.S            @exit                            ; if so, bag it.
  240.             
  241. ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
  242. ; routine in order to tie it in.  In order to set up the parameters for the call, I have to do a stack
  243. ; crawl, to get the previous return addresses.  The problem of course is that the stack crawl has
  244. ; no known length, and is often less than kCrawlArraySize.  So... I have to look at each one as
  245. ; it is created, and make sure that it is valid; and continue no farther when I find a bad one.  If
  246. ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases.  I will call
  247. ; the routine, where the stackToAdd is an array, built on the stack:
  248. ;    PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
  249. ;        VAR treeTop, emptyQ: TrackEntryPtr);
  250.  
  251.             MOVE.L        saveEmSize(SP),D1        ; retrieve size parameter.
  252.             
  253.             MOVEQ        #kCrawlArraySize-1,D0    ; for 8 times in DBRA loop.
  254. @Clr
  255.             CLR.L            -(SP)                            ; make a stack crawl array on the stack,
  256.             DBRA        D0,@Clr                        ; by looping 8 times. (all zeroed.)
  257.             
  258.             MOVE.L        SP,A1                            ; the address of the stack crawl array.
  259.             
  260. ; Now with that array built and zeroed I want to add the parameters to the stack.  If I have to 
  261. ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have 
  262. ; already been zeroed, which marks them as non-valid.  I'm going to set up for the call 
  263. ; here, so the registers are freed up for doing the funky stack crawl checks.
  264.  
  265.             MOVE.L        A0,-(SP)                        ; addressToAdd: block being added to tracker.
  266.             MOVE.L        D1,-(SP)                        ; sizeToAdd: size of block being tracked.
  267.             MOVE.L        A1,-(SP)                        ; stackToAdd: the address of the array of crawls.
  268.             PEA            pTreeTop                    ; treeTop: bTree top.      (as var parameters)
  269.             PEA            pEmptyQ                        ; emptyQ: top of empties list. 
  270.             
  271. ; All the parameters are set up, do the stack crawl checks.  This will modify the stackToAdd
  272. ; array, for each crawl that is valid.  The stackToAdd is still in A1.
  273.  
  274.             MOVE.L        A6,A0                        ; get stack frame head.
  275.             MOVEQ        #kCrawlArraySize-1,D0    ; doing a loop for 8 stack crawls.
  276.             
  277. @Frame
  278.             CMP.L         A7,A0                        ; make sure it is greater than a7
  279.             BLO.S            @noFrame                    ; skip any more if not valid.
  280.             CMP.L        CurStackBase,A0            ; make sure it is less than base of stack,
  281.             BHI.S            @noFrame                    ; If not, skip checking rest of crawl.
  282.  
  283. ; It's a valid frame, go ahead and save off the return address in the array.  A1 is the address of
  284. ; the next array element to set.  It is autoincremented to the next entry each time I can stow a
  285. ; valid pc return address.
  286.  
  287.             MOVE.L        4(A0),(A1)+                    ; the pc return address for this frame, into Array.
  288.             
  289.             MOVE.L        (A0),A0                        ; next stack frame back. (as a big chain)
  290.             DBRA        D0,@Frame                    ; loop for all 8.
  291.             
  292. ; If there was a bogus frame in there somewhere, I bailed out to here.  All the rest of the stackArray
  293. ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway.  I got here if
  294. ; all the entries were valid, too, and just fell out of the DBRA loop.  
  295.  
  296. @noFrame
  297.             BSR            AddNewBlock                ; track a new block on heap.
  298.             ADD.L        #kCrawlArraySize*4,SP    ; kill the crawl array from stack.
  299. @exit
  300.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  301.             ADDQ        #4,SP                            ; kill size that was saved.     (from D0)
  302.             RTS                                            ; Return to caller of NewPtr.
  303.  
  304.  
  305. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  306. ; WatchDisposPtr:
  307. ;    This routine is my patch to DisposPtr, and it watches the address being passed in
  308. ;    A0 to see if it is one of the blocks I am tracking in the table.  If it is, I release that
  309. ;    record from my b-Tree code, since it is obviously not a leak when it is released.  
  310. ;    If the block is not found, then I just skip it too, since there are presumably a 
  311. ;    ton of blocks that I didn't see being allocated, since I'm not watching from the 
  312. ;    start of the system.  This is a head patch, since I do my thing, then jump to
  313. ;    the old routine.  When that routine finishes, it will call RTS and go back to
  314. ;    wherever I was called from.
  315.  
  316.             export        WatchDisposPtr
  317.             
  318. WatchDisposPtr
  319.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  320.  
  321.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  322.             BEQ.S            CallOldDisposePtr            ; if not, skip saving.
  323.             
  324.             MOVE.L        A0,-(SP)                        ; block being added to tracker.
  325.             PEA            pTreeTop                    ; bTree top.      (as var parameters)
  326.             PEA            pEmptyQ                        ; top of empties list. 
  327.             BSR            KillOldBlock                ; get it out of my dang table
  328.  
  329. CallOldDisposePtr
  330.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  331.  
  332.             MOVE.L        pOldDisposPtr,-(SP)        ; get address of old routine.
  333.             RTS                                            ; and jump there.
  334.                                                             ; when it RTSes, I'll go back to the caller.
  335.                         
  336.  
  337. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  338.  
  339. ; WatchNewHandle:
  340. ;    The routine to patch the NewHandle trap, and I get first dibs at getting the info I want
  341. ;    out of the registers.  I need to save off the size to start with, since D0 gets pounded
  342. ;    to the return result once I actually call the old NewHandle code.  Once I have that
  343. ;    safely stowed on the stack, I call the old code, using the PC-relative addressing in
  344. ;    order to get the old address that I'm supposed to call.  This was set up when the
  345. ;    dcmd called me to save off the value.  When that old NewHandle junk returns, I get
  346. ;     the actual address allocated, if enough memory, then do the funky stack crawl,
  347. ;    and finally pass all these tidbits off to the AddNewBlock routine.  The AddNewBlock
  348. ;    doesn't differentiate between pointers being watched or handles being watched, it is
  349. ;     just an address. 
  350.  
  351.             export        WatchNewHandle
  352.             
  353.  
  354. WatchNewHandle
  355.             MOVE.L        D0,-(SP)                        ; save size off.
  356.  
  357. ; First, allocate the handle.
  358.  
  359.             PEA            @1                            ; coming back here, as return address.
  360.             MOVE.L        pOldNewHandle,-(SP)        ; the old address I am going to,
  361.             RTS                                            ; JMP there, when it RTSes I come back
  362. @1                                                        ; --here.
  363.  
  364. ; A0 is set up as the handle to track, add it to the b-tree list.  If it is NIL, meaning the
  365. ; NewHandle failed, then don't add it to the list, it cannot be disposed.  I do the save of
  366. ; registers here, after the allocation of memory, so that I can save the registers as they
  367. ; are coming out of the call, not going in.
  368.  
  369.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  370.  
  371.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  372.             BEQ.S            @exit                            ; if not, skip saving.
  373.             CMP.L        #0,A0                            ; is it a NIL return?
  374.             BEQ.S            @exit                            ; if so, bag it.
  375.             
  376. ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
  377. ; routine in order to tie it in.  In order to set up the parameters for the call, I have to do a stack
  378. ; crawl, to get the previous return addresses.  The problem of course is that the stack crawl has
  379. ; no known length, and is often less than kCrawlArraySize.  So... I have to look at each one as
  380. ; it is created, and make sure that it is valid; and continue no farther when I find a bad one.  If
  381. ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases.  I will call
  382. ; the routine, where the stackToAdd is an array, built on the stack:
  383. ;    PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
  384. ;        VAR treeTop, emptyQ: TrackEntryPtr);
  385.  
  386.             MOVE.L        saveEmSize(SP),D1        ; retrieve size parameter.
  387.             
  388.             MOVEQ        #kCrawlArraySize-1,D0    ; for 8 times in DBRA loop.
  389. @Clr
  390.             CLR.L            -(SP)                            ; make a stack crawl array on the stack,
  391.             DBRA        D0,@Clr                        ; by looping 8 times. (all zeroed.)
  392.             
  393.             MOVE.L        SP,A1                            ; the address of the stack crawl array.
  394.             
  395. ; Now with that array built and zeroed I want to add the parameters to the stack.  If I have to 
  396. ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have 
  397. ; already been zeroed, which marks them as non-valid.  I'm going to set up for the call 
  398. ; here, so the registers are freed up for doing the funky stack crawl checks.
  399.  
  400.             MOVE.L        A0,-(SP)                        ; addressToAdd: block being added to tracker.
  401.             MOVE.L        D1,-(SP)                        ; sizeToAdd: size of block being tracked.
  402.             MOVE.L        A1,-(SP)                        ; stackToAdd: the address of the array of crawls.
  403.             PEA            pTreeTop                    ; treeTop: bTree top.      (as var parameters)
  404.             PEA            pEmptyQ                        ; emptyQ: top of empties list. 
  405.             
  406. ; All the parameters are set up, do the stack crawl checks.  This will modify the stackToAdd
  407. ; array, for each crawl that is valid.  The stackToAdd is still in A1.
  408.  
  409.             MOVE.L        A6,A0                        ; get stack frame head.
  410.             MOVEQ        #kCrawlArraySize-1,D0    ; doing a loop for 8 stack crawls.
  411.             
  412. @Frame
  413.             CMP.L         A7,A0                        ; make sure it is greater than a7
  414.             BLO.S            @noFrame                    ; skip any more if not valid.
  415.             CMP.L        CurStackBase,A0            ; make sure it is less than base of stack,
  416.             BHI.S            @noFrame                    ; If not, skip checking rest of crawl.
  417.  
  418. ; It's a valid frame, go ahead and save off the return address in the array.  A1 is the address of
  419. ; the next array element to set.  It is autoincremented to the next entry each time I can stow a
  420. ; valid pc return address.
  421.  
  422.             MOVE.L        4(A0),(A1)+                    ; the pc return address for this frame, into Array.
  423.             
  424.             MOVE.L        (A0),A0                        ; next stack frame back. (as a big chain)
  425.             DBRA        D0,@Frame                    ; loop for all 8.
  426.             
  427. ; If there was a bogus frame in there somewhere, I bailed out to here.  All the rest of the stackArray
  428. ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway.  I got here if
  429. ; all the entries were valid, too, and just fell out of the DBRA loop.  
  430.  
  431. @noFrame
  432.             BSR            AddNewBlock                ; track a new block on heap.
  433.             ADD.L        #kCrawlArraySize*4,SP    ; kill the crawl array from stack.
  434. @exit
  435.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  436.             ADDQ        #4,SP                            ; kill size that was saved.     (from D0)
  437.             RTS                                            ; Return to caller of NewPtr.
  438.  
  439.  
  440. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  441. ; WatchDisposHandle:
  442. ;    This routine is my patch to DisposHandle, and it watches the address being passed in
  443. ;    A0 to see if it is one of the blocks I am tracking in the table.  If it is, I release that
  444. ;    record from my b-Tree code, since it is obviously not a leak when it is released.  
  445. ;    If the block is not found, then I just skip it too, since there are presumably a 
  446. ;    ton of blocks that I didn't see being allocated, since I'm not watching from the 
  447. ;    start of the system.  This is a head patch, since I do my thing, then jump to
  448. ;    the old routine.  When that routine finishes, it will call RTS and go back to
  449. ;    wherever I was called from.
  450.  
  451.             export        WatchDisposHandle
  452.             
  453. WatchDisposHandle
  454.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  455.  
  456.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  457.             BEQ.S            CallOldDisposeHandle    ; if not, skip saving.
  458.             
  459.             MOVE.L        A0,-(SP)                        ; block being added to tracker.
  460.             PEA            pTreeTop                    ; bTree top.      (as var parameters)
  461.             PEA            pEmptyQ                        ; top of empties list. 
  462.             BSR            KillOldBlock                ; get it out of my dang table
  463.  
  464. CallOldDisposeHandle
  465.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  466.  
  467.             MOVE.L        pOldDisposHandle,-(SP)    ; get address of old routine.
  468.             RTS                                            ; and jump there.
  469.                                                             ; when it RTSes, I'll go back to the caller.
  470.                         
  471.             endproc
  472.  
  473. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  474.  
  475.             end
  476.